package hip.util.file;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.UrlResource;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

public class FileUtil {
    public static Logger logger = Logger.getLogger(FileUtil.class);

    @SuppressWarnings("resource")
    public synchronized static void writeTo(String str, String filepath) {
        FileChannel fout = null;
        try {
            //先判断路径是否存在
            File f = new File(filepath);
            if (!f.exists()) {
                File pf = f.getParentFile();
                if (!pf.exists()) {
                    pf.mkdirs();
                }
                f.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(filepath);
            fout = fos.getChannel();
            byte[] b = str.getBytes(StandardCharsets.UTF_8);
            ByteBuffer buf = ByteBuffer.allocate(b.length);
            buf.clear();
            buf.put(b);
            buf.flip();
            while (buf.hasRemaining()) {
                fout.write(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("写入文件" + filepath + "失败");
        } finally {
            try {
                if (fout != null) {
                    fout.close();
                }
            } catch (Exception ioe) {
                throw new RuntimeException("关闭文件通道失败");
            }
        }
    }

    public static Reader readerFrom(String filepath) {
        FileInputStream fis = null;
        BufferedReader br = null;
        try {
            fis = new FileInputStream(filepath);
            InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
            br = new BufferedReader(isr);
            return br;
        } catch (Exception e) {
            throw new RuntimeException("读取文件" + filepath + "失败");
        }
    }

    public static InputStream readInFrom(String filepath) {
        BufferedInputStream bi = null;
        try {
            bi = new BufferedInputStream(Files.newInputStream(Paths.get(filepath)));
            return bi;
        } catch (Exception e) {
            throw new RuntimeException("读取文件" + filepath + "失败");
        }
    }

    /**
     * 读取xml文件
     */
    public static Document readDocFrom(String filePath) {
        SAXReader reader = new SAXReader();
        Document document = null;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(filePath);
            document = reader.read(fis);
        } catch (FileNotFoundException e) {
            return null;
        } catch (DocumentException de) {
            throw new RuntimeException("读取xml文件失败，路径: " + filePath + " 原因：" + de.getMessage());
        } finally {
            try {
                if (null != fis) {
                    fis.close();
                    System.gc();
                    //Thread.sleep(1000);
                }
            } catch (Exception e) {
                throw new RuntimeException("关闭xml文件失败");
            }
        }
        return document;
    }

    /**
     * 读取xml文件
     */
    public static Document readDocFrom(InputStream in) throws Exception {
        SAXReader reader = new SAXReader();
        Document document = null;
        try {
            document = reader.read(in);
        } catch (DocumentException de) {
            throw new RuntimeException("读取xml文件失败，原因：" + de.getMessage());
        } finally {
            try {
                if (null != in) {
                    in.close();
                    System.gc();
                    //Thread.sleep(1000);
                }
            } catch (Exception e) {
                throw new RuntimeException("关闭xml文件失败");
            }
        }
        return document;
    }

    /**
     * @param relativePath:jar包内文件的相对路径
     */
    public static Document readDoc(String relativePath) throws IOException, DocumentException {
        Enumeration<URL> urls = FileUtil.class.getClassLoader().getResources(relativePath);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource iss = new UrlResource(url);
            SAXReader saxReadr = new SAXReader();
            org.dom4j.Document doc = saxReadr.read(iss.getInputStream());
            return doc;
        }
        return null;
    }

    /**
     * 文件删除
     *
     * @param path
     */
    public synchronized static void remove(String path) {
        File f = new File(path);
        if (f.exists() && f.isDirectory()) {//判断是文件还是目录
            if (Objects.requireNonNull(f.listFiles()).length == 0) {//若目录下没有文件则直接删除
                f.delete();
            } else {//若有则把文件放进数组，并判断是否有下级目录
                File delFile[] = f.listFiles();
                int i = Objects.requireNonNull(f.listFiles()).length;
                for (int j = 0; j < i; j++) {
                    assert delFile != null;
                    if (delFile[j].isDirectory()) {
                        remove(delFile[j].getAbsolutePath());//递归调用del方法并取得子目录路径
                    }
                    delFile[j].delete();//删除文件
                }
                f.delete();//最后删除自身目录
            }
        } else if (f.exists() && f.isFile()) {
            f.delete();
        }
    }

    /**
     * 删除特定后缀文件
     *
     * @param path
     */
    public synchronized static void removeWithSuffix(String path, final String suffix) {
        File filePath = new File(path);
        if (!filePath.exists()) {
            logger.info("目录不存在!");
        } else {
            if (filePath.isDirectory()) {
                File[] list = filePath.listFiles(new FilenameFilter() {
                    public boolean accept(File dir, String name) {
                        return name.endsWith(suffix);
                    }
                });
                for (int i = 0; i < list.length; i++) {
                    list[i].delete();
                }
            }
        }
    }

    public static List<File> getChildFiles(String path) {
        List<File> cFiles = new ArrayList<>();
        File f = new File(path);
        if (f.exists() && f.isDirectory()) {//判断是文件还是目录
            if (f.listFiles().length > 0) {//若有则把文件放进数组，并判断是否有下级目录
                File[] files = f.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (!files[i].isDirectory()) {
                        cFiles.add(files[i]);
                    }
                }
            }
        }
        return cFiles;
    }

    public static void getFilesByName(String path, String name, List<File> cFiles) {
        File f = new File(path);
        if (f.exists() && f.isDirectory()) {//判断是文件还是目录
            if (f.listFiles().length > 0) {//若有则把文件放进数组，并判断是否有下级目录
                File[] files = f.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (!files[i].isDirectory()) {
                        if (files[i].getName().equals(name) || files[i].getName().split("\\.")[0].equals(name)) {//是否所需文件
                            cFiles.add(files[i]);
                        }
                    } else {
                        getFilesByName(files[i].getAbsolutePath(), name, cFiles);
                    }
                }
            }
        }
    }

    /**
     * 下载
     *
     * @param request
     * @param response
     * @param contentType
     */
    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String contentType, String fileName, String downLoadPath) throws Exception {
        downloadFile(request, response, contentType, fileName, Files.newInputStream(Paths.get(downLoadPath)));
    }

    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String contentType, String fileName, InputStream stream) throws Exception {
        //response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            if (request.getHeader("user-agent").toLowerCase().indexOf("msie") > 0) {
                fileName = URLEncoder.encode(fileName, "UTF8");
            } else {
                fileName = new String(fileName.getBytes("utf-8"), "ISO8859-1");
            }

            response.setContentType(contentType);
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);//  //
            response.setHeader("Content-Length", String.valueOf(stream.available()));
            bis = new BufferedInputStream(stream);
            bos = new BufferedOutputStream(response.getOutputStream());
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }

            bis.close();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bis != null) bis.close();
            if (bos != null) bos.close();
        }
    }

    public static String uploadFile(HttpServletRequest request, HttpServletResponse response, String filename) throws IOException {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile multipartFile = multipartRequest.getFile(filename);
        String sourceName = multipartFile.getOriginalFilename(); // 原始文件名
        String fileType = sourceName.substring(sourceName.lastIndexOf("."));

        logger.info("上传的文件名为:" + sourceName + "类型为:" + fileType);
        String base = request.getSession().getServletContext().getRealPath("/") + "attachments" + File.separator + "uploadedExcel";
        File file = new File(base);
        if (!file.exists()) {
            file.mkdirs();
        }

        response.setContentType("text/json; charset=UTF-8");
        String path = base + File.separator + sourceName;
        multipartFile.transferTo(new File(path));
        return path;
    }

    /**
     * 根据相对路径获取绝对路径（文件需要在classes下）
     */
    public static String getPath(String relativePath) {
        String path;
        try {
            String ipath = Thread.currentThread().getContextClassLoader().getResource(relativePath).toURI().getPath();
            if (ipath.startsWith("/")) {
                path = ipath.substring(1);
            } else {
                path = ipath;
            }
        } catch (Exception e) {
            throw new RuntimeException("路径错误!");
        }
        return path;
    }

    /**
     * 获取项目绝对路径
     */
    public static String getPath() {
        File f = new File(FileUtil.class.getResource("/").getPath());
        String path = f.getParentFile().getParent() + File.separator;
        return path;
    }

    /**
     * 获取jar包的路径
     */
    public static String getJarPath() throws UnsupportedEncodingException {
        String path = FileUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        path = java.net.URLDecoder.decode(path, "utf-8");
        return path;
    }

    /**
     * @param relativePath:jar包内文件的相对路径
     */
    public static InputStream getStream(String relativePath) throws IOException, DocumentException {
        Enumeration<URL> urls = FileUtil.class.getClassLoader().getResources(relativePath);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource iss = new UrlResource(url);
            return iss.getInputStream();
        }
        return null;
    }

    public static String getPathByName(String rootPath, String filename) {
        List<File> files = new ArrayList<>();
        FileUtil.getFilesByName(rootPath, filename, files);
        String path = null;
        if (files.get(0) != null) {
            path = files.get(0).getAbsolutePath();
        }
        return path;
    }

    public synchronized static boolean renameFile(String oldName, String newName) {
        boolean isOk = false;
        File srcDir = new File(oldName);
        File targetDir = new File(newName);
        if (srcDir.exists() && !targetDir.exists()) {
            isOk = srcDir.renameTo(targetDir);
        }
        logger.info("rename:" + isOk);
        return isOk;
    }

    public synchronized static boolean mkFolder(String path) {
        File f = new File(path);
        if (!f.exists()) {
            return f.mkdirs();
        }
        return false;
    }

    /**
     * 复制整个文件夹内容
     *
     * @param oldPath String 原文件路径 如：c:/fqf
     * @param newPath String 复制后路径 如：f:/fqf/ff
     * @return boolean
     */
    public static void copyFolder(String oldPath, String newPath) {
        try {
            (new File(newPath)).mkdir(); // 如果文件夹不存在 则建立新文件夹
            File a = new File(oldPath);
            String[] file = a.list();
            File temp = null;
            for (int i = 0; i < file.length; i++) {
                if (oldPath.endsWith(File.separator)) {
                    temp = new File(oldPath + file[i]);
                } else {
                    temp = new File(oldPath + File.separator + file[i]);
                }

                if (temp.isFile()) {
                    FileInputStream input = new FileInputStream(temp);
                    FileOutputStream output = new FileOutputStream(newPath + "/" + (temp.getName()).toString());

                    byte[] b = new byte[1024 * 5];
                    int len;
                    while ((len = input.read(b)) != -1) {
                        output.write(b, 0, len);
                    }
                    output.flush();
                    output.close();
                    input.close();
                }
                if (temp.isDirectory()) {// 如果是子文件夹
                    copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i]);
                }
            }
        } catch (Exception e) {
            logger.error("复制整个文件夹内容操作出错");
            e.printStackTrace();
        }
    }

    /**
     * 判断某文件夹下是否存在文件，存在返回true
     *
     * @return
     */
    public static boolean existInDir(String path) {
        boolean flag = false;
        File filePath = new File(path);
        if (filePath.exists()) {
            if (filePath.isDirectory()) {
                File[] list = filePath.listFiles();
                if (list != null && list.length != 0) {
                    flag = true;
                }
            }
        }
        return flag;
    }

}
